package org.test4j.mock.startup;

import org.test4j.mock.MockUp;
import org.test4j.mock.faking.util.ClassFile;
import org.test4j.mock.faking.util.ReflectUtility;
import org.test4j.mock.faking.util.StackTrace;

import java.util.*;

import static java.util.Arrays.asList;
import static org.test4j.mock.faking.util.ClassLoad.loadClass;

final class MockInitialization {
    private MockInitialization() {
    }

    static void initialize() {
        ClassFile.initPrintFakes();
        applyStartupSpiFakesAsNeeded();
        applyUserSpecifiedStartupFakesIfAny();
    }

    /**
     * 启动时初始化框架级mock行为
     */
    private static void applyStartupSpiFakesAsNeeded() {
        ServiceLoader<IStartupMock> loader = ServiceLoader.load(IStartupMock.class);
        for (Iterator<IStartupMock> it = loader.iterator(); it.hasNext(); ) {
            IStartupMock startupMock = it.next();
            startupMock.initial();
        }
    }

    private static void applyUserSpecifiedStartupFakesIfAny() {
        Collection<String> fakeClasses = getFakeClasses();
        for (String fakeClassName : fakeClasses) {
            applyStartupFake(fakeClassName);
        }
    }

    private static Collection<String> getFakeClasses() {
        String commaOrSpaceSeparatedValues = System.getProperty("fakes");
        if (commaOrSpaceSeparatedValues == null) {
            return Collections.emptyList();
        }

        //noinspection DynamicRegexReplaceableByCompiledPattern
        String[] fakeClassNames = commaOrSpaceSeparatedValues.split("\\s*,\\s*|\\s+");
        Set<String> uniqueClassNames = new HashSet<>(asList(fakeClassNames));
        uniqueClassNames.remove("");
        return uniqueClassNames;
    }

    private static void applyStartupFake(String fakeClassName) {
        String argument = null;
        int p = fakeClassName.indexOf('=');
        if (p > 0) {
            argument = fakeClassName.substring(p + 1);
            fakeClassName = fakeClassName.substring(0, p);
        }
        try {
            Class fakeClass = loadClass(fakeClassName);
            if (MockUp.class.isAssignableFrom(fakeClass)) {
                ReflectUtility.newInstance(fakeClass, argument);
            }
        } catch (UnsupportedOperationException ignored) {
        } catch (Throwable unexpectedFailure) {
            StackTrace.filterStackTrace(unexpectedFailure);
            unexpectedFailure.printStackTrace();
        }
    }
}